﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace WCAD.Kean
{
    public class DashedTextFinder
    {
        // Stores our global overrule instance
        private static TextHighlightOverrule _tho = null;
        [CommandMethod("EDT")]
        public static void EraseDashedText()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;
            var db = doc.Database;
            var count = 0;
            // In case the SDT command hasn't been run, we need to create
            // our custom overrule object
            if (_tho == null)
                _tho = new TextHighlightOverrule();
            using (var tr = doc.TransactionManager.StartTransaction())
            {
                // Let's iterate through the BlockTable, erasing the
                // relevant entities from the various BlockTableRecords
                var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (var btrId in bt)
                {
                    var btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);
                    foreach (var entId in btr)
                    {
                        // Also open erased objects, although we won't process
                        // them, of course
                        var ent = (Entity)tr.GetObject(entId, OpenMode.ForRead, true);
                        if (!ent.IsErased)
                        {
                            // If the entity is a BlockReference, check its
                            // AttributeReferences, otherwise just check the
                            // entity itself using our overrule object
                            if (ent is BlockReference)
                            {
                                var br = (BlockReference)ent;
                                foreach (var aId in br.AttributeCollection)
                                {
                                    var ar = (AttributeReference)tr.GetObject((ObjectId)aId, OpenMode.ForRead, true);
                                    if (!ar.IsErased && _tho.IsApplicable(ar))
                                    {
                                        ar.UpgradeOpen();
                                        ar.Erase();
                                        count++;
                                    }
                                }
                            }
                            else if (_tho.IsApplicable(ent))
                            {
                                ent.UpgradeOpen();
                                ent.Erase();
                                count++;
                            }
                        }
                    }
                }
                tr.Commit();
            }
            // As we've erased all "problematic" entities, we might also
            // remove the overrule...
            //_tho.Remove();
            //_tho.Dispose();
            //_tho = null;
            ed.WriteMessage("\nErased {0} blank or dashed text entit{1}.", count, count == 1 ? "y" : "ies");
        }
        // Highlights the dashed text objects in the drawing
        [CommandMethod("HDT")]
        public static void HighlightDashedText()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            if (_tho == null)
                _tho = new TextHighlightOverrule();
            short newCol = UpdateHighlightOverrule(doc, _tho);
            if (newCol < 0)
            {
                _tho.Dispose();
                _tho = null;
            }
            else
                _tho.HighlightColor = newCol;
        }
        // A helper function which applies our highlight overrule
        // to different types, adjusting the color index used
        private static short UpdateHighlightOverrule(Document doc, HighlightOverrule ho)
        {
            // Ask the user for the new color index to use
            var opts = new PromptIntegerOptions("\nEnter highlight color index: ");
            opts.LowerLimit = 0;
            opts.UpperLimit = 127;
            opts.Keywords.Add("Clear");
            opts.DefaultValue = ho.HighlightColor;
            var res = doc.Editor.GetInteger(opts);
            if (res.Status == PromptStatus.Keyword)
            {
                // If the Clear keyword was entered, let's remove the
                // overrule
                if (res.StringResult == "Clear")
                {
                    ho.Remove();
                    doc.Editor.Regen();
                    return -1;
                }
            }
            else if (res.Status == PromptStatus.OK)
            {
                // Otherwise we attach the overrule for each type
                ho.Add();
                // If requested highlight color is a new color, then we
                // want to change it
                if (ho.HighlightColor != res.Value)
                    ho.HighlightColor = (short)res.Value;
                doc.Editor.Regen();
            }
            return (short)res.Value;
        }
    }
    // Custom base class with a highlight text property
    public class HighlightOverrule : DrawableOverrule
    {
        private Type[] _types;
        public HighlightOverrule(short col = 1, Type[] entTypes = null)
        {
            _types = entTypes;
            _color = col;
        }
        // Color index used to highlight
        private short _color;
        // The color we highlight blocks with
        public short HighlightColor
        {
            get { return _color; }
            set
            {
                if ((value >= 0) && (value <= 127))
                {
                    _color = value;
                }
            }
        }
        public void Add()
        {
            // Call Overrule.Remove for each of the passed-in types
            foreach (Type t in _types)
            {
                // Use try-catch, as Overrule.HasOverrule() needs an
                // instance of an AutoCAD object, and we just have the
                // type
                try
                {
                    Overrule.AddOverrule(RXObject.GetClass(t), this, false);
                }
                catch
                { }
            }
        }
        public void Remove()
        {
            // Call Overrule.Remove for each of the passed-in types
            foreach (Type t in _types)
            {
                // Use try-catch, as Overrule.HasOverrule() needs an
                // instance of an AutoCAD object, and we just have the
                // type
                try
                {
                    Overrule.RemoveOverrule(RXObject.GetClass(t), this);
                }
                catch
                { }
            }
        }
    }
    // Overrule to highlight text (MText and DBText) objects
    public class TextHighlightOverrule : HighlightOverrule
    {
        public TextHighlightOverrule(short col = 1, Type[] types = null) : base(col, types == null ? new Type[] { typeof(DBText), typeof(MText) } : types)
        {
            SetCustomFilter();
        }
        public override bool WorldDraw(Drawable drawable, WorldDraw wd)
        {
            // Registered for MText and DBText, so proceed cautiously
            var ent = drawable as Entity;
            if (ent != null)
            {
                var mt = ent as MText;
                var dt = ent as DBText;
                if (mt != null || dt != null)
                {
                    var norm = (mt == null ? dt.Normal : mt.Normal);
                    // Now we want to draw a box around the extents
                    var ext = ent.Bounds;
                    if (ext.HasValue)
                    {
                        var maxPt = ext.Value.MaxPoint;
                        var minPt = ext.Value.MinPoint;
                        // These are the vertices of the highlight box
                        // (it also contains a cross, for fun)
                        var pts = new Point3dCollection(
                            new Point3d[]{new Point3d(minPt.X, minPt.Y, minPt.Z),
                  new Point3d(maxPt.X, maxPt.Y, maxPt.Z)});
                        // Store old graphics color and lineweight
                        short oldColor = wd.SubEntityTraits.Color;
                        wd.SubEntityTraits.Color = this.HighlightColor;
                        var oldLineweight = wd.SubEntityTraits.LineWeight;
                        wd.SubEntityTraits.LineWeight = LineWeight.LineWeight070;
                        // Draw the polyline
                        wd.Geometry.Polyline(pts, norm, IntPtr.Zero);
                        // Restore old settings
                        wd.SubEntityTraits.LineWeight = oldLineweight;
                        wd.SubEntityTraits.Color = oldColor;
                    }
                }
            }
            // Let the overruled Drawable draw itself
            return base.WorldDraw(drawable, wd);
        }
        public override bool IsApplicable(RXObject overruledSubject)
        {
            // Extract the string we need to check from the types
            // of entity this overrule works on (DBText & MText)
            var stringToCheck = "";
            var dt = overruledSubject as DBText;
            if (dt != null)
            {
                stringToCheck = dt.TextString;
            }
            var mt = overruledSubject as MText;
            if (mt != null)
            {
                stringToCheck = mt.Text;
            }
            return IsDashedOrWhitespace(stringToCheck);
        }
        private static bool IsDashedOrWhitespace(string text)
        {
            if (String.IsNullOrEmpty(text))
                return false;
            // Use a simple Regular Expression to check whether
            // the string passed in consists only of spaces and/or
            // underlines
            return Regex.Match(text, "^[ _]+$", RegexOptions.IgnoreCase).Success;

        }
    }
}
